/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 * File Name     : roce_db.c
 * Version       : v2.0
 * Created       : 2021/3/12
 * Last Modified : 2021/12/7
 * Description   : The definition of functions for RoCE DB.
 */

#include <linux/kernel.h>
#if defined(CENTOS8_2_4_18_0)
#include <rdma/uverbs_ioctl.h>
#endif

#include "roce.h"

#ifdef LLT_TEST
#include "llt_roce_stub.h"
#endif

static void roce3_db_page(struct roce3_ucontext *context, unsigned long virt, struct roce3_db_page *db_page)
{
#if (defined(OFED_MLNX_5_4) || defined(OFED_MLNX_5_7_8) || defined(CENTOS8_2_4_18_0)) && \
    (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))
    struct ib_ucontext *ibucontext = &context->ibucontext;
    struct uverbs_attr_bundle *attrs = container_of(&ibucontext, struct uverbs_attr_bundle, context);
#endif

    db_page->user_virt = (virt & PAGE_MASK);
    db_page->refcnt = 0;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
    db_page->umem = ib_umem_get(context->ibucontext.device, virt & PAGE_MASK, PAGE_SIZE, 0);
#elif defined(OFED_MLNX_5_7_8) || defined(OFED_MLNX_5_4)
    db_page->umem = ib_umem_get(&attrs->driver_udata, virt & PAGE_MASK, PAGE_SIZE, 0);
#elif defined(FEATURE_UMEM_UDA_5)
    db_page->umem = ib_umem_get(&attrs->driver_udata, virt & PAGE_MASK, PAGE_SIZE, 0, 0);
#else
    db_page->umem = ib_umem_get(&context->ibucontext, virt & PAGE_MASK, PAGE_SIZE, 0, 0);
#endif
}

/* ****************************************************************************
 Prototype    : roce3_db_map_user
 Description  : roce3_db_map_user
 Input        : struct roce3_ucontext *context
                unsigned long virt
                struct roce3_db *db
 Output       : None

  1.Date         : 2015/4/29
    Modification : Created function

**************************************************************************** */
int roce3_db_map_user(struct roce3_ucontext *context, unsigned long virt, struct roce3_db *db)
{
    int ret = 0;
    struct roce3_db_page *db_page = NULL;

    mutex_lock(&context->db_page_mutex);

    list_for_each_entry(db_page, &context->db_page_list, list) {
        if (db_page->user_virt == (virt & PAGE_MASK)) {
            goto found;
        }
    }

    db_page = (struct roce3_db_page *)kmalloc(sizeof(*db_page), GFP_KERNEL);
    if (db_page == NULL) {
        ret = -ENOMEM;
        pr_err("[ROCE, ERR] %s: Failed to alloc DB page\n", __func__);
        goto out;
    }

    roce3_db_page(context, virt, db_page);

    if (IS_ERR(db_page->umem)) {
        ret = (int)PTR_ERR(db_page->umem);
        pr_err("[ROCE, ERR] %s: Failed to get ib_umem ret:%d\n", __func__, ret);
        kfree(db_page);
        goto out;
    }

    list_add(&db_page->list, &context->db_page_list);

found:
#if (defined(HAVE_KS_SUPPORT) && (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0))) || \
    (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))
    db->dma = sg_dma_address(db_page->umem->sgt_append.sgt.sgl) + (virt & ~PAGE_MASK);
#else
    db->dma = sg_dma_address(db_page->umem->sg_head.sgl) + (virt & ~PAGE_MASK);
#endif
    db->user_page = db_page;
    ++db_page->refcnt;

out:
    mutex_unlock(&context->db_page_mutex);

    return ret;
}

/* ****************************************************************************
 Prototype    : roce3_db_unmap_user
 Description  : roce3_db_unmap_user
 Input        : struct roce3_ucontext *context
                struct roce3_db *db
 Output       : None

  1.Date         : 2015/4/29
    Modification : Created function

**************************************************************************** */
void roce3_db_unmap_user(struct roce3_ucontext *context, struct roce3_db *db)
{
    mutex_lock(&context->db_page_mutex);

    if ((--db->user_page->refcnt) == 0) {
        list_del(&db->user_page->list);
        ib_umem_release(db->user_page->umem);
        kfree(db->user_page);
    }

    mutex_unlock(&context->db_page_mutex);
}
